#include <os/list.h>
#include <os/lock.h>
#include <os/sched.h>
#include <os/time.h>
#include <os/mm.h>
#include <screen.h>
#include <printk.h>
#include <assert.h>

pcb_t pcb[NUM_MAX_TASK];
const ptr_t pid0_stack = INIT_KERNEL_STACK + PAGE_SIZE;
pcb_t pid0_pcb = {
    .pid = 0,
    .kernel_sp = (ptr_t)pid0_stack,
    .user_sp = (ptr_t)pid0_stack
};

LIST_HEAD(ready_queue);
LIST_HEAD(sleep_queue);

/* current running task PCB */
pcb_t * volatile current_running;

/* global process id */
pid_t process_id = 1;

void enqueue(list_head* queue, pcb_t* item)
{
    list_add_tail(&item->list, queue);
}



pcb_t * delete_queue(list_head* queue){
    pcb_t* tmp = list_entry(queue->next, pcb_t, list);
    // list_del(queue->next);
    queue->next = queue->next->next;
    queue->next->prev = queue;
    return tmp;
}

void add_queue(list_head* queue, pcb_t* item){
    list_add_tail(&item->list, queue);
}


void do_scheduler(void)
{
    // TODO: [p2-task3] Check sleep queue to wake up PCBs

    check_sleeping();
    // TODO: [p2-task1] Modify the current_running pointer.
    pcb_t *last_running = current_running;
    pcb_t *next_running = delete_queue(&ready_queue);
    assert(ready_queue.next != next_running);
    if(current_running->status == TASK_RUNNING && current_running->pid != 0)
    {
        current_running->status = TASK_READY; 
        add_queue(&ready_queue, current_running);
    }
    // TODO: [p2-task1] switch_to current_running
    next_running->status = TASK_RUNNING;
    current_running = next_running;
    switch_to(last_running, next_running);
}

void do_sleep(uint32_t sleep_time)
{
    // TODO: [p2-task3] sleep(seconds)
    // NOTE: you can assume: 1 second = 1 `timebase` ticks
    // 1. block the current_running
    // 2. set the wake up time for the blocked task
    // 3. reschedule because the current_running is blocked.
    current_running->status = TASK_BLOCKED;
    enqueue(&sleep_queue, current_running);
    current_running->wakeup_time = get_timer() + sleep_time;
    do_scheduler();
}

void do_block(list_node_t *pcb_node, list_head *queue)
{
    // TODO: [p2-task2] block the pcb task into the block queue
    // 第一步，将要阻塞的进程加入队列之中
    pcb_t *Thepcb = list_entry(pcb_node, pcb_t, list);
    // ready_queue.prev = ready_queue.prev->prev;
    // ready_queue.prev->next = &ready_queue;
    enqueue(queue, Thepcb);
    // 第二步，将阻塞的进程的状态进行改变
    Thepcb->status = TASK_BLOCKED;
    do_scheduler();
}

void do_unblock(list_node_t *pcb_node)
{
    // TODO: [p2-task2] unblock the `pcb` from the block queue
    pcb_t *GetThePcb = delete_queue(pcb_node);
    GetThePcb->status = TASK_READY;
    ready_queue.prev->next = &GetThePcb->list;
    GetThePcb->list.prev = ready_queue.prev;
    GetThePcb->list.next = &ready_queue;
    ready_queue.prev = &GetThePcb->list;
}
